const fs = require('fs');
const path = require('path');
const $ = require('jquery');
// const TorrentShareInfo = require('../../index').TorrentShareInfo;
// const TorrentManager = require('../../index').TorrentManager;
// const Torrent = require('../../index').Torrent;

const TorrentShareInfo = require('./lib/torrent-share-info').TorrentShareInfo;
const TorrentManager = require('./lib/torrent-manager').TorrentManager;
const Torrent = require('./lib/torrent').Torrent;


const MAX_TIME = 60;
let torrentManager = null;
let torrent = null;
let stopUpdateChart = false;

$(window.document).ready(function () {
  const $peerId = $('#peer_id')
    , $refreshBtn = $('.refresh-btn')
    , $shareDialogue = $('.share-dialogue')
    , $downloadDialogue = $('.download-dialogue');

  console.log('获取websocket token...');
  innerServices.webbtConfigerAPI.get({}, res => {
    torrentManager = new TorrentManager(res.data);
    torrentManager.once('ready', peerId => {
      console.log('Torrent Manger is ready.');
      $peerId.text(peerId);
      tip('');
      $shareDialogue.removeAttr('hidden');
      $downloadDialogue.removeAttr('hidden');
    });
    torrentManager.on('error', err => {
      errTip(err);
    });
  });

  // 刷新前要关闭 torrentManager
  $(window).on('beforeunload', () => {
    torrentManager.close();
  });

  // 刷新页面
  $refreshBtn.on('click', () => {
    location.reload();
  });

  // 选择一个本地文件分享
  const $shareBtn = $shareDialogue.find('.share-btn');
  $shareBtn.on('click', () => {
    const $fileInput = $shareDialogue.find('.file-input')
      , $torrentShareInfo = $shareDialogue.find('.torrent-share-info')
      , files = $fileInput[0].files;

    if (files.length === 0) {
      alert('请选择一个有效的文件!');
      return;
    }

    const shareFile = files[0]
      , filePath = shareFile.path
      , torrentFilePath = path.join(path.dirname(filePath), `${shareFile.name}.torrent-info`);

    $downloadDialogue.remove();
    $shareBtn.remove();
    $fileInput.attr('disabled', true);
    tip(`生成 ${torrentFilePath}...`);
    TorrentShareInfo.generate(shareFile.path, (err, torrentShareInfo) => {
      if (err) {
        alert(err);
        return;
      }
      $torrentShareInfo.html(syntaxHighlight(torrentShareInfo));
      saveTorrentShareInfoToFile(torrentShareInfo, torrentFilePath, (err) => {
        if (err) {
          errTip(err.toString);
          throw err;
        }
        createTorrentDialogue(torrentFilePath, filePath);
      });
    });
  });

  // 通过torrentShareInfo下载
  const downloadDir = '/tmp/';
  $downloadDialogue.find('.download-dir').text(downloadDir);
  const $downloadBtn = $downloadDialogue.find('.download-btn');
  $downloadBtn.on('click', () => {
    const $torrentShareInfoInput = $downloadDialogue.find('.torrent-share-info')
      , data = $torrentShareInfoInput.val();
    let torrentShareInfo = null;
    if (!data) {
      alert('请粘贴TorrentShareInfo!');
      return;
    }
    try {
      torrentShareInfo = JSON.parse(data);
    } catch (error) {
      alert('非JSON格式!');
      return;
    }
    filePath = path.join(downloadDir, torrentShareInfo.info.name);
    $shareDialogue.remove();
    $torrentShareInfoInput.attr('disabled', true);
    $downloadBtn.remove();

    createTorrentDialogue(torrentShareInfo, filePath);
  });
});

function tip(text) {
  const $msg = $('.top-tip');
  $msg.html(`<span style="color: #000099">${text}</span>`);
};

function errTip(text) {
  const $msg = $('.top-tip');
  $msg.html(`<span style="color: #ff0000">Error: ${text}</span>`);
}

/**
 * 保存 torrent share info 到文件中
 */
function saveTorrentShareInfoToFile(torrentShareInfo, savePath, cb) {
  const data = JSON.stringify(torrentShareInfo, null, 2);
  fs.writeFile(savePath, data, cb);
}

/**
 *  Torrent
 */
function createTorrentDialogue(torrentShareInfo, filePath) {
  const $torrentDialogue = $('.torrent-dialogue'),
    $pauseBtn = $torrentDialogue.find('.pause-btn'),
    $stopBtn = $torrentDialogue.find('.stop-update-chart-btn'),
    $deleteBtn = $torrentDialogue.find('.delete-btn')

  tip('初始化...');
  try {
    torrent = new Torrent(torrentShareInfo, filePath);
  } catch (err) {
    errTip(err.toString);
    throw err;
  }
  torrent.once('ready', () => {
    tip('开始下载/分享...');
    drawUpAndDownloadChart(torrent);
    drawpeersChart(torrent);
    drawBitFieldChart(torrent);
    $torrentDialogue.removeAttr('hidden');
  });
  torrent.once('error', err => {
    errTip(err.toString);
    throw err;
  });
  torrent.once('done', () => {
    tip('下载完成,开始分享...');
  });
  torrentManager.addTorrent(torrent);

  // 按钮 暂停/开始 下载
  $pauseBtn.on('click', () => {
    if (torrent.isPaused) {
      torrent.resume();
      $pauseBtn.text('暂停下载');
    } else {
      torrent.pause();
      $pauseBtn.text('继续下载');
    }
  });

  // 按钮 停止/自动更新 chart
  $stopBtn.on('click', () => {
    if (stopUpdateChart) {
      $stopBtn.text('停止刷新');
    } else {
      $stopBtn.text('自动刷新');
    }
    stopUpdateChart = !stopUpdateChart;
  });

  // 按钮 删除
  $deleteBtn.on('click', () => {
    const b = confirm('删除该文件?');
    if (!b) return;
    tip('删除中...');
    torrent.once('delete', () => {
      tip(`文件已删除, 3s后刷新页面...`);
      setTimeout(() => {
        location.reload();
      }, 3000);
    });
    torrent.delete();
  });
}

/**
 * 绘制下载上传统计图
 */
function drawUpAndDownloadChart(torrent) {
  const upAndDownloadChart = echarts.init(document.getElementById('up-and-down-chart'));
  let downSpeeds = []
    , uploadSpeeds = []

  const option = {
    title: {
      text: '下载与上传线性图',
      subtext: '',
      x: 'center'
    },
    tooltip: {
      trigger: 'item',
      formatter: function (params, ticket, callback) {
        const seriesName = params.seriesName;
        return `${seriesName}: ${add_suffix(params.data)}/s`
      }
    },
    animation: false,
    legend: {
      data: ['下载速度', '上传速度'],
      x: 'right'
    },
    grid: {
      left: '3%',
      right: '4%',
      bottom: '3%',
      containLabel: true
    },
    toolbox: {
      feature: {
        saveAsImage: {}
      }
    },
    xAxis: {
      show: false,
      type: 'category',
      boundaryGap: false,
      data: []
    },
    yAxis: {
      name: 'byte/s',
      type: 'value'
    },
    series: [{
        name: '下载速度',
        type: 'line',
        stack: '总量',
        itemStyle: {
          normal: {
            color: `#00FF00`,
            lineStyle: {
              color: `#00FF00`
            }
          }
        },
        data: []
      },
      {
        name: '上传速度',
        type: 'line',
        stack: '总量',
        itemStyle: {
          normal: {
            color: `#FFCC00`,
            lineStyle: {
              color: `#FFCC00`,
              type: 'dotted'
            }
          }
        },
        data: []
      }
    ]
  };
  upAndDownloadChart.setOption(option);

  let interval = setInterval(() => {
    const stats = torrent.getStats();
    downSpeeds.push(stats.downloadSpeed);
    uploadSpeeds.push(stats.uploadSpeed);

    if (downSpeeds.length > MAX_TIME || uploadSpeeds.length > MAX_TIME) {
      downSpeeds = downSpeeds.slice(downSpeeds.length - MAX_TIME);
      uploadSpeeds = uploadSpeeds.slice(uploadSpeeds.length - MAX_TIME);
    }

    if (stopUpdateChart) return;
    upAndDownloadChart.setOption({
      title: {
        subtext: `下载: ${add_suffix(stats.downloaded)}(${add_suffix(stats.downloadSpeed)}/s)    上传: ${add_suffix(stats.uploaded)}(${add_suffix(stats.uploadSpeed)}/s)`
      },
      series: [{
          data: downSpeeds
        }, {
          data: uploadSpeeds
        }
      ]
    });
  }, 1000);

  torrent.once('close', () => {
    clearInterval(interval);
  });
  torrent.once('ready', () => {
    clearInterval(interval);
  });
}

/**
 * 绘制peers
 */
function drawpeersChart(torrent) {
  let peersChart = echarts.init(document.getElementById('peers-chart'))
    , peerStatsDict = {}
    , timeStamps = []

  const option = {
    title: [{
      left: 'center',
      text: '远端节点'
    }],
    tooltip: {
      trigger: 'item',
      formatter: function (params, ticket, callback) {
        const data = params.data,
          seriesName = params.seriesName,
          peerIdShort = seriesName.substr(0, 8) + '...' + seriesName.substr(seriesName.length - 4),
          amChoking = data[2],
          amInterested = data[3],
          peerChoking = data[4],
          peerInterested = data[5],
          requestsCount = data[6],
          peerRequestsCount = data[7],
          uploaded = data[8],
          downloaded = data[9],
          uploadSpeed = data[10],
          downloadSpeed = data[11]
        const htmlStr = `<div>
          <header>${seriesName}</header>
          <div style="padding: 5px 10px">
            <span>下载: ${add_suffix(downloaded)} 上传: ${add_suffix(uploaded)}</span><br>
            <span>下载速度: ${add_suffix(downloadSpeed)}/s  上传速度: ${add_suffix(uploadSpeed)}/s</span><br>
            <span>状态: ${amChoking ? 'C' : ' '} ${amInterested ? 'I' : ' '} ${peerChoking ? 'c' : ' '} ${peerInterested ? 'i': ' '}</span><br>
            <span>本地请求缓存: ${requestsCount}  远端请求缓存: ${peerRequestsCount}</span><br>
          </div>
        </div>`;
        return htmlStr;
      }
    },
    animation: false,
    xAxis: [{
      type: 'time',
      show: false,
      data: [],
    }, {
      type: 'time',
      show: false,
      data: [],
      gridIndex: 1
    }],
    yAxis: [{
      name: '(从远端下载)byte/s',
    }, {
      name: '(上传到远端)byte/s',
      inverse: true,
      gridIndex: 1
    }],
    grid: [{
      left: '3%',
      right: '4%',
      containLabel: true,
      bottom: '50%'
    }, {
      left: '3%',
      right: '4%',
      containLabel: true,
      top: '50%'
    }],
    series: []
  };
  peersChart.setOption(option);

  let interval = setInterval(() => {
    let curPeers = torrent._wires
      , now = Date.now()
      , minTimeStamp = null;

    timeStamps.push(now);
    if (timeStamps.length > MAX_TIME) {
      timeStamps = timeStamps.slice(timeStamps.length - MAX_TIME);
    }
    minTimeStamp = timeStamps[0];

    for (let peer of curPeers) {
      const peerId = peer.peerId;
      if (!(peerId in peerStatsDict)) peerStatsDict[peerId] = [];

      peerStatsDict[peerId].push({
        timeStamp: now,
        amChoking: peer.amChoking,
        amInterested: peer.amInterested,
        peerChoking: peer.peerChoking,
        peerInterested: peer.peerInterested,
        requestsCount: peer.requests.length,
        peerRequestsCount: peer.peerRequests.length,
        uploaded: peer.uploaded,
        downloaded: peer.downloaded,
        uploadSpeed: peer.uploadSpeed(),
        downloadSpeed: peer.downloadSpeed(),
      });
    }

    const series = [];
    for (let peerId in peerStatsDict) {
      let peerStats = peerStatsDict[peerId]
        , itemDown = {}
        , itemUp = {}
        , color = peerId.slice(34);

      itemDown.name = itemUp.name = peerId;
      itemDown.type = itemUp.type = 'line';
      itemDown.itemStyle = {
        normal: {
          color: `#${color}`,
          lineStyle: {
            color: `#${color}`
          }
        }
      };
      itemUp.itemStyle = {
        normal: {
          color: `#${color}`,
          lineStyle: {
            color: `#${color}`,
            type: 'dotted'
          }
        }
      };

      // itemDown.showSymbol = itemUp.showSymbol = false;
      itemUp.xAxisIndex = 1;
      itemUp.yAxisIndex = 1;
      itemDown.data = [];
      itemUp.data = [];

      let index = -1;
      for (let i = 0; i < peerStats.length; i ++) {
        const d = peerStats[i];
        if (d.timeStamp >= minTimeStamp) {
          break;
        }
        index = i;
      }
      if (index !== -1) {
        peerStats = peerStats.slice(index + 1);
        peerStatsDict[peerId] = peerStats;
      }
      for (let i = 0; i < peerStats.length; i ++) {
        const d = peerStats[i];
        itemDown.data.push([d.timeStamp, d.downloadSpeed, d.amChoking, d.amInterested, d.peerChoking, d.peerInterested, d.requestsCount, d.peerRequestsCount, d.uploaded, d.downloaded, d.uploadSpeed, d.downloadSpeed]);
        itemUp.data.push([d.timeStamp, d.uploadSpeed, d.amChoking, d.amInterested, d.peerChoking, d.peerInterested, d.requestsCount, d.peerRequestsCount, d.uploaded, d.downloaded, d.uploadSpeed, d.downloadSpeed])
      }
      series.push(itemDown);
      series.push(itemUp);
    }

    if (stopUpdateChart) return;
    peersChart.setOption({
      title: {
        subtext: `连接数: ${torrent._wires.length}   队列: ${torrent._queue.length}`
      },
      xAxis: [{ data: timeStamps }, { data: timeStamps, gridIndex: 1 }],
      series: series
    });
  }, 1000);

  torrent.once('close', () => {
    clearInterval(interval);
  });
  torrent.once('ready', () => {
    clearInterval(interval);
  });
}

/**
 * 绘制BitField 分段图
 */
function drawBitFieldChart(torrent) {
  const bitfieldChart = echarts.init(document.getElementById('bitfield-chart'))
    , data = bitfieldTo3dData(torrent.bitField, torrent.filePieceCount)

  const option = {
    title: {
      text: 'BitField 分段图',
      subtext: `共${torrent.filePieceCount}片 (每片${add_suffix(torrent.filePieceSize)}包含${torrent.filePieceSize/16384}块,每块16k)`,
      x: 'center'
    },
    animation: false,
    tooltip: {
      trigger: 'item',
      formatter: function (params, ticket, callback) {
        const index = params.data[3]
          , value = params.data[2];
        if (value === 0) return `${index} 未完成`;
        else if (value === 1) return `${index} 完成`;
        else return `${index} 下载中`;
      }
    },
    grid: {
      //height: '50%',
      y: '55',
      y2: '40',
      x: '20',
      x2: '20'
    },
    xAxis: {
      type: 'category',
      data: [],
      show: false,
    },
    yAxis: {
      inverse: true,
      type: 'category',
      data: [],
      show: false,
    },
    visualMap: {
      type: 'piecewise',
      splitNumber: 4,
      pieces: [{
          gte: 0,
          lte: 0,
          color: 'rgba(211,211,211,0.7)'
        },
        {
          gte: 1,
          lte: 1,
          color: 'rgba(30,144,255,0.7)',
        },
        {
          gte: 2,
          lte: 2,
          color: 'rgba(	255,165,0,0.7)',
        }
      ],
      dimension: 2,
      align: 'left',
      show: true,
      //calculable: true,
      orient: 'horizontal', //'vertical'  horizontal
      left: 'right', //center
      top: 'top',
      bottom: '15%'
    },
    series: [{
      name: 'channel',
      type: 'heatmap',
      data: data,
      itemStyle: {
        emphasis: {
          shadowBlur: 10,
          shadowColor: 'rgba(0, 0, 0, 0.5)'
        }
      }
    }]
  };
  bitfieldChart.setOption(option);

  torrent.on('piece-selected', pieceIndex => {
    data[pieceIndex][2] = 2;
  });
  torrent.on('piece-finished', pieceIndex => {
    data[pieceIndex][2] = 1;
  });

  let interval = setInterval(() => {
    if (stopUpdateChart) return;
    bitfieldChart.setOption({ series: [{ data: data }] });
  }, 1000);

  function bitfieldTo3dData(bitField, pieceCount) {
    const colCount = rowCount = Math.ceil(Math.sqrt(pieceCount)),
      data = [];

    for (let row = 0; row < rowCount; row++) {
      for (let col = 0; col < colCount; col++) {
        const index = row * rowCount + col,
          value = index < pieceCount ? (bitField.get(index) ? 1 : 0) : -1;
        data.push([col, row, value, index]);
      }
    }
    return data;
  }
}

function add_suffix(val, s=1024) {
  const prefix = ['B', 'KB', 'MB', 'GB', 'TB'];
  for (let i = 0; i < prefix.length; i++) {
    if (Math.abs(val) < s) {
      return val.toFixed(2) + prefix[i]
    }
    val = parseInt(val / s)
  }
  return val.toFixed(2) + 'PB'
}
